package com.gan.simple_init.common.easyexcellistner;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.read.listener.ReadListener;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.gan.simple_init.model.movie.dto.AddMovieDto;
import com.gan.simple_init.model.movie.entity.Movie;
import com.gan.simple_init.service.MovieService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.BeanUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * @author gbw
 * @date 2023/10/25
 * 有失败的就不上传数据，返回所有错误信息
 */
@Slf4j
public class MovieNotIgnoreFailListener implements ReadListener<AddMovieDto> {

    /**
     * 上传失败条数
     */
    public int failedNum = 0;


    /**
     * 上传失败原因
     */
    public StringBuilder failedInfo = new StringBuilder();

    /**
     * 缓存的数据
     */
    private List<Movie> movieList = new ArrayList<>();

    /**
     * 假设这个是一个DAO，当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private MovieService movieService;

    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param movieService
     */
    public MovieNotIgnoreFailListener(MovieService movieService) {
        this.movieService = movieService;
    }

    /**
     * 类型转换报错
     *
     * @param exception
     * @param context
     * @throws Exception
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        log.error("解析失败，但是不继续解析下一行:{}", exception.getMessage());
        // 如果是某一个单元格的转换异常 能获取到具体行号
        // 如果要获取头的信息 配合invokeHeadMap使用
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
            String errorInfo = String.format("第%s行，第%s列数据类型转换异常，数据为:%s", excelDataConvertException.getRowIndex(),
                    excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData().getStringValue());
            log.error(errorInfo);
            failedInfo.append("错误" + (++failedNum) + "：" + errorInfo + ";/n");
        }
    }

    /**
     * 解析到的每一条数据对象
     *
     * @param movieDto
     * @param analysisContext
     */
    @Override
    public void invoke(AddMovieDto movieDto, AnalysisContext analysisContext) {
        Movie movie = new Movie();
        BeanUtils.copyProperties(movieDto,movie);
        String validInfo = movieService.validMovieInfo(movie);
        // 这一行的对象不能通过校验就记录错误信息并且不入库，否则加到入库的队列中
        if (validInfo != null) {
            failedInfo.append("错误" + (++failedNum) + "：" + "错误行：" +
                    analysisContext.getCurrentRowNum() + "；错误信息：" + validInfo + ";/n");
        } else {
            movieList.add(movie);
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // 全部数据解析完成后再入库
        saveOrUpdate();
    }

    /**
     * 自定义入库策略，这边以 电影名称 + 导演名 作为唯一标识，有则更新无则插入
     */
    private void saveOrUpdate() {
        log.info("{}条数据，开始存储数据库！", movieList.size());
        // 如果有错误数据就全都不入库
        if (failedNum != 0){
            return;
        }
        movieList.forEach(movie -> {
            String director = movie.getDirector();
            String title = movie.getTitle();
            movieService.saveOrUpdate(movie, new UpdateWrapper<Movie>()
                    .eq(ObjectUtils.isNotEmpty(director), "director", director)
                    .eq(ObjectUtils.isNotEmpty(title), "title", title));
        });
        log.info("存储数据库成功！");
    }
}
